package ar.uade.fain.ai.tpo.dao;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;

import ar.uade.fain.ai.tpo.model.Cliente;

public class ClienteDAO extends CacheDAOSupport<Cliente> {

	private static final ClienteMapper MAPPER = new ClienteMapper();

	private static final String SELECT_CLIENTES = 
			"SELECT " +
			"	C.IDCLIENTE" +
			", 	C.NOMBRE" +
			", 	C.APELLIDO" +
			", 	C.TELEFONO" +
			", 	C.DOMICILIO" +
			",	C.FRECUENTE " +
			"FROM DELIVERY.DBO.CLIENTE C ";

	private static final String SELECT_NEXT_ID = 
			"SELECT ISNULL(MAX(IDCLIENTE),0) + 1 AS ID " +
			"FROM DELIVERY.DBO.CLIENTE";

	private static final String SELECT_CLIENTE_BY_ID = 
			SELECT_CLIENTES + "WHERE C.IDCLIENTE = ?";
	
	private static final String SELECT_CLIENTE_BY_NOMBRE_APELLIDO = 
			SELECT_CLIENTES + "WHERE C.NOMBRE = ? AND C.APELLIDO = ?";
	
	private static final String INSERT_CLIENTE = 
			"INSERT INTO DELIVERY.DBO.CLIENTE (" +
			"	IDCLIENTE, NOMBRE, APELLIDO, TELEFONO, DOMICILIO, FRECUENTE" +
			") VALUES (?,?,?,?,?,?)";

	private static final String UPDATE_CLIENTE = 
			"UPDATE DELIVERY.DBO.CLIENTE SET " +
			"	NOMBRE = ?" +
			", 	APELLIDO = ?" +
			",	TELEFONO = ?" +
			",	DOMICILIO = ?" +
			",	FRECUENTE = ? " +
			"WHERE IDCLIENTE = ?";
	
	public ClienteDAO() {
		super();
	}
	
	public Cliente getByNombre(final String nombre, final String apellido) {
		final Set<Cliente> enCache = buscarEnCache(new Selector<Cliente>() {
			@Override
			public boolean aceptar(final Cliente entidad) {
				return entidad.getNombre().equalsIgnoreCase(nombre) 
						&& entidad.getApellido().equalsIgnoreCase(apellido);
			}
		});
		if (enCache.isEmpty()) {
			return cargar(nombre, apellido);
		} else {
			return enCache.iterator().next();
		}
	}

	private Cliente cargar(final String nombre, final String apellido) {
		final List<Cliente> enBase = database().select(
				SELECT_CLIENTE_BY_NOMBRE_APELLIDO,
				new ParameterSetter() {
			@Override
			public void setParametros(final PreparedStatement statement) 
					throws SQLException {
				statement.setString(1, nombre == null ? "" : nombre);
				statement.setString(2, apellido == null ? "" : apellido); 
			}
		}, MAPPER);
		if (enBase.isEmpty()) {
			return null;
		} else {
			final Cliente cliente = enBase.get(0);
			agregarACache(cliente);
			return cliente;
		}
	}
	
	public Cliente getByID(final Long id) {
		final Set<Cliente> enCache = buscarEnCachePorID(id);
		if (enCache.isEmpty()) {
			return cargar(id);
		} else {
			return enCache.iterator().next();
		}
	}

	private Set<Cliente> buscarEnCachePorID(final Long id) {
		final Set<Cliente> enCache = buscarEnCache(new Selector<Cliente>() {
			@Override
			public boolean aceptar(final Cliente entidad) {
				return entidad.getId().equals(id);
			}
		});
		return enCache;
	}
	
	private Cliente cargar(final Long id) {
		final List<Cliente> results = database().select(
				SELECT_CLIENTE_BY_ID, id, MAPPER);
		if (results.isEmpty()) {
			return null;	
		} else {
			final Cliente cliente = results.get(0);
			agregarACache(cliente);
			return cliente;
		}
	} 
	
	public void guardar(final Cliente cliente) {
		final Long nextId = database().selectNextId(SELECT_NEXT_ID);
		database().insert(INSERT_CLIENTE, new ParameterSetter() {
			@Override
			public void setParametros(final PreparedStatement statement) 
					throws SQLException {
				statement.setLong(1, nextId);
				statement.setString(2, cliente.getNombre());
				statement.setString(3, cliente.getApellido());
				statement.setString(4, cliente.getTelefono());
				statement.setString(5, cliente.getDomicilio());
				statement.setBoolean(6, cliente.isFrecuente());
			}
		});
		agregarACache(cliente);
	}
	
	public void actualizar(final Cliente cliente) {
		database().update(UPDATE_CLIENTE, new ParameterSetter() {
			@Override
			public void setParametros(final PreparedStatement statement) 
					throws SQLException {
				statement.setString(1, cliente.getNombre());
				statement.setString(2, cliente.getApellido());
				statement.setString(3, cliente.getTelefono());
				statement.setString(4, cliente.getDomicilio());
				statement.setBoolean(5, cliente.isFrecuente());
				statement.setLong(6, cliente.getId());
			}
		});
		agregarACache(cliente);
	}
	
	private static class ClienteMapper implements RowMapper<Cliente> {
		
		public ClienteMapper() {
			super();
		}
		
		@Override
		public Cliente map(final ResultSet rs) throws SQLException {
			final Cliente cliente = new Cliente(rs.getLong("idcliente"));
			cliente.setNombre(rs.getString("nombre"));
			cliente.setApellido(rs.getString("apellido"));
			cliente.setDomicilio(rs.getString("domicilio"));
			cliente.setTelefono(rs.getString("telefono"));
			cliente.setFrecuente(rs.getBoolean("frecuente"));
			return cliente;
		}
		
	}
	
}
